// Package middlewares
// Created by GoLand
// @User: lenora
// @Date: 2023/12/2
// @Time: 16:32

package middlewares

import (
	ijwt "camp/microbook/internal/web/jwt"
	"encoding/gob"
	"fmt"
	"github.com/gin-contrib/sessions"
	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt/v5"
	"log"
	"net/http"
	"strings"
	"time"
)

const (
	SessionKey = "userId"
	updateKey  = "updateTime"
)

type MiddlewareBuilder struct {
}

func (m *MiddlewareBuilder) CheckLogin() gin.HandlerFunc {
	// !important
	// session存储在redis里面,但go的time类型在redis里面并不被支持,所以需要转成字节,使用到了gob包,具体方式为在gob包中注册time类型
	gob.Register(time.Now())
	return func(context *gin.Context) {
		path := context.Request.URL.Path
		if path == "/users/signup" || path == "/users/login" {
			return
		}
		sess := sessions.Default(context)
		userId := sess.Get(SessionKey)
		if userId == nil {
			//未登录,停止执行后续业务逻辑
			context.AbortWithStatus(http.StatusUnauthorized)
			return
		}
		now := time.Now()
		updated := sess.Get(updateKey)
		lastUpdated, ok := updated.(time.Time)
		if updated == nil || !ok || now.Sub(lastUpdated) > time.Minute {
			//首次设置updated,第一次进入的时间
			sess.Set(updated, now)
			//sessions包的坑,本次没有设置的key会被覆盖掉,所以userId的那个key需要再设置一次
			sess.Set(SessionKey, userId)
			sess.Options(sessions.Options{MaxAge: 60 * 10})
			if err := sess.Save(); err != nil {
				fmt.Println(err)
			}
		}
	}
}

type JwtMiddlewareBuilder struct {
	handler ijwt.Handler
}

func NewJwtMiddlewareBuilder(handler ijwt.Handler) *JwtMiddlewareBuilder {
	return &JwtMiddlewareBuilder{handler: handler}
}

func (m *JwtMiddlewareBuilder) CheckLogin() gin.HandlerFunc {
	return func(context *gin.Context) {
		path := context.Request.URL.Path
		if path == "/users/signup" ||
			path == "/users/login" ||
			path == "/sms/send" ||
			path == "/users/login_sms" ||
			path == "oauth2/wechat/auth_url" ||
			path == "oauth2/wechat/callback" {
			return
		}

		auth := context.GetHeader("Authorization")
		if auth == "" {
			//未登录,停止执行后续业务逻辑
			context.AbortWithStatus(http.StatusUnauthorized)
			return
		}

		segs := strings.Split(auth, " ")
		if len(segs) != 2 || segs[0] != "Bearer" {
			context.AbortWithStatus(http.StatusUnauthorized)
			return
		}
		var uc ijwt.UserClaims
		token, err := jwt.ParseWithClaims(segs[1], &uc, func(token *jwt.Token) (interface{}, error) {
			return ijwt.JWTKey, nil
		})
		if err != nil {
			//获取token失败,token错误
			log.Println("parse jwt error:", err)
			context.AbortWithStatus(http.StatusUnauthorized)
			return
		}
		if !token.Valid {
			// token解析成功 但token有可能非法  也有可能已过期
			context.AbortWithStatus(http.StatusUnauthorized)
			return
		}

		if uc.UserAgent != context.GetHeader("User-Agent") {
			log.Println("warning:web agent is changed")
			context.AbortWithStatus(http.StatusUnauthorized)
			return
		}

		//但其实如果已过期的话,token.Valid的值就会是false,那这个判定其实不判定也可以
		if uc.ExpiresAt.Before(time.Now()) {
			//token已过期
			context.AbortWithStatus(http.StatusUnauthorized)
			return
		}

		//验证对应用户是否已退出登录
		if err := m.handler.CheckSession(context, uc.Ssid); err != nil {
			context.AbortWithStatus(http.StatusUnauthorized)
			return
		}

		context.Set("web", uc)
	}
}
